<?php
/**
 * crcl/base
 *
 * Copyright 2013 CRCL - André Sabosch - info@crcl.de
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

declare(encoding = 'UTF-8');
namespace crcl\base\template;

interface ICompiler
{
  /**
   * Returns compiled template
   *
   * @return string
   */
  public function compile();

  /**
   * Sets values
   *
   * @param $aValues
   */
  public function setValues($aValues);
}

class Compiler extends \crcl\base\xml\sax\Builder implements ICompiler
{
  public function setValues($aValues)
  {
    $this->aValues = $aValues;
  }

  public function compile()
  {
    $this->parse();
    $sContent = $this->getBuild();

    return $sContent;
  }

  //parser events (@see sax/Builder)
  public function onProcessingInstruction($sInstruction)
  {
    $sReturn = $sInstruction;
    if (\substr($sInstruction, 0, 6) == '<?base')
    {
      $aAttributes = $this->getAttributes($sInstruction);
      //var_dump($aAttributes);

      if (isset($aAttributes['content']))
      {
        $oInstruction = Instruction::factory(Instruction::TYPE_CONTENT);
      }
      if (isset($aAttributes['include']))
      {
        $oInstruction = Instruction::factory(Instruction::TYPE_INCLUDE);
      }

      $oInstruction->setAttributes($aAttributes);
      $oInstruction->setValues($this->aValues);
      $oInstruction->verify();
      $oInstruction->execute();
      $sReturn = $oInstruction->getOutput();
    }
    parent::onProcessingInstruction($sReturn);
  }

  public function onElementStart($sTagName, $aAttributes, $bShort = \FALSE)
  {
    parent::onElementStart($sTagName, $aAttributes, $bShort);
  }

  public function onXmlDeclaration($sDeclaration)
  {
    //do nothing ;)
  }

  /** PRIVATE *************************************************************************************/

  private $aValues = array();

  private function getAttributes($sInstruction)
  {
    $sInstruction = \str_replace('?>', '', $sInstruction);
    $sInstruction = \str_replace('<?base', '', $sInstruction);
    $sInstruction = \trim($sInstruction);

    $bAttribute = \TRUE;
    $bValue = \FALSE;
    $sAttribute = '';
    $sValue = '';
    $aReturn = array();
    for ($i=0; $i<\strlen($sInstruction); $i++)
    {
      if ($bValue)
      {
        if ($sInstruction[$i] == '"') // end of value
        {
          $bValue = \FALSE;
          $aReturn[$sAttribute] = \trim($sValue);
          continue;
        }
        else
        {
          $sValue .= $sInstruction[$i];
        }
      }
      if (!$bValue && ($sInstruction[$i] == ' ')) //new attribute
      {
        $bAttribute = \TRUE;
        $bValue = \FALSE;
        $sAttribute = '';
        $sValue = '';
        continue;
      }
      if ($sInstruction[$i] == '=')
      {
        $bAttribute = \FALSE;
        continue;
      }
      if ($bAttribute)
      {
        $sAttribute .= $sInstruction[$i];
      }
      if (!$bAttribute)
      {
        if ($sInstruction[$i] == '"') //start of value
        {
          $bValue = \TRUE;
        }
        else
        {
          if (!$bValue) //missing "
          {
            throw new Exception(Exception::ERROR_COMPILER_MISSING_QUOTATIONMARKS,
              array('sProcessingInstruction' => $sInstruction, 'sAttribute' => $sAttribute));
          }
        }
      }
    }
    return $aReturn;
  }
}